package com.mo.axion.handler.handler.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.CharSequenceUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.google.common.base.Throwables;
import com.mo.axion.common.constant.CommonConstant;
import com.mo.axion.common.domain.RecallTaskInfo;
import com.mo.axion.common.domain.TaskInfo;
import com.mo.axion.common.dto.account.sms.SmsAccount;
import com.mo.axion.common.dto.model.ContentModel;
import com.mo.axion.common.dto.model.SmsContentModel;
import com.mo.axion.common.enums.ChannelType;
import com.mo.axion.handler.domain.sms.MessageTypeSmsConfig;
import com.mo.axion.handler.domain.sms.SmsParam;
import com.mo.axion.handler.handler.BaseHandler;
import com.mo.axion.handler.handler.Handler;
import com.mo.axion.handler.script.SmsScript;
import com.mo.axion.support.dao.SmsRecordDao;
import com.mo.axion.support.domain.SmsRecord;
import com.mo.axion.support.service.ConfigService;
import com.mo.axion.support.utils.AccountUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;

/**
 * @author Mo
 * @createTime 2024/4/12 22:32
 * @description 短信渠道分发器
 */
@Slf4j
@Component
public class SmsHandler extends BaseHandler implements Handler {

    /**
     * 流量自动分配策略
     */
    private static final Integer AUTO_FLOW_RULE = 0;
    private static final String FLOW_KEY = "msgTypeSmsConfig";
    private static final String FLOW_KEY_PREFIX = "message_type_";

    @Autowired
    private SmsRecordDao smsRecordDao;

    @Autowired
    private ConfigService configService;

    @Autowired
    private ApplicationContext applicationContext;

    @Autowired
    private AccountUtils accountUtils;

    public SmsHandler() {
        channelCode = ChannelType.SMS.getCode();
    }

    /**
     * 统一处理handler接口
     *
     * @param taskInfo
     * @return
     */
    @Override
    public boolean handler(TaskInfo taskInfo) {
        //  0. 构建短信下发参数
        SmsParam smsParam = SmsParam.builder()
                .phones(taskInfo.getReceiver())
                .content(getSmsContent(taskInfo))
                .messageTemplateId(taskInfo.getMessageTemplateId())
                .build();
        try {
            //  1. 动态配置流量负载，根据消息类型加载短信渠道的账号配置，取出主要下发渠道和备用下发渠道
            MessageTypeSmsConfig[] messageTypeSmsConfigs = loadBalance(getMessageTypeSmsConfig(taskInfo));
            //  2. 优先使用主要下发渠道配置对消息下发，若下发失败则使用备用下发渠道配置将短信下发
            for (MessageTypeSmsConfig messageTypeSmsConfig : messageTypeSmsConfigs) {
                smsParam.setScriptName(messageTypeSmsConfig.getScriptName());
                smsParam.setSendAccountId(messageTypeSmsConfig.getSendAccount());
                List<SmsRecord> recordList = applicationContext.getBean(messageTypeSmsConfig.getScriptName(), SmsScript.class).send(smsParam);
                if (CollUtil.isNotEmpty(recordList)) {
                    //  3. 发送短信成功，入库记录
                    smsRecordDao.saveAll(recordList);
                    return true;
                }
            }
        } catch (Exception e) {
            log.error("SmsHandler#handler failed:{}, params:{}", Throwables.getStackTraceAsString(e), JSON.toJSONString(smsParam));
        }
        return false;
    }

    /**
     * 流量负载
     * 根据配置的权重优先连接某个账号，并取出一个备份的
     * @param messageTypeSmsConfigList
     * @return
     */
    private MessageTypeSmsConfig[] loadBalance(List<MessageTypeSmsConfig> messageTypeSmsConfigList) {
        int total = 0;
        for (MessageTypeSmsConfig messageTypeSmsConfig : messageTypeSmsConfigList) {
            total += messageTypeSmsConfig.getWeights();
        }

        //  生成一个1~total的随机数
        int index = new Random().nextInt(total) + 1;
        MessageTypeSmsConfig supplier = null;
        MessageTypeSmsConfig supplierBack = null;
        for (int i = 0; i < messageTypeSmsConfigList.size(); i++) {
            if (index <= messageTypeSmsConfigList.get(i).getWeights()) {
                supplier = messageTypeSmsConfigList.get(i);

                int j = (i + 1) % messageTypeSmsConfigList.size();
                if (j == i) {
                    return new MessageTypeSmsConfig[]{supplier};
                }
                supplierBack = messageTypeSmsConfigList.get(j);
                return new MessageTypeSmsConfig[]{supplier, supplierBack};
            }
            index -= messageTypeSmsConfigList.get(i).getWeights();
        }
        return new MessageTypeSmsConfig[0];
    }

    /**
     * 如果模板指定具体的明确账号，则优先发送其账号，否则走流量配置
     *
     * 流量配置每种类型都会有其下发渠道账号的配置
     * @param taskInfo
     * @return
     */
    private List<MessageTypeSmsConfig> getMessageTypeSmsConfig(TaskInfo taskInfo) {
        /**
         * 如果模板指定了账号，优先使用指定的账号发送消息
         */
        if (!taskInfo.getSendChannel().equals(AUTO_FLOW_RULE)) {
            SmsAccount account = accountUtils.getAccountById(taskInfo.getSendAccount(), SmsAccount.class);
            return Arrays.asList(
                    MessageTypeSmsConfig.builder()
                            .sendAccount(taskInfo.getSendAccount())
                            .scriptName(account.getScriptName())
                            .weights(100).build()
            );
        }
        /**
         * 读取Apollo流量配置
         */
        String property = configService.getProperty(FLOW_KEY, CommonConstant.EMPTY_VALUE_JSON_ARRAY);
        JSONArray jsonArray = JSON.parseArray(property);
        for (int i = 0; i < jsonArray.size(); i++) {
            JSONArray array = jsonArray.getJSONObject(i).getJSONArray(FLOW_KEY_PREFIX + taskInfo.getMsgType());
            if (CollUtil.isNotEmpty(array)) {
                return JSON.parseArray(JSON.toJSONString(array), MessageTypeSmsConfig.class);
            }
        }
        return new ArrayList<>();
    }

    /**
     * 如果有链接，则把链接拼在文案后
     * @param taskInfo
     * @return
     */
    private String getSmsContent(TaskInfo taskInfo) {
        SmsContentModel smsContentModel = (SmsContentModel) taskInfo.getContentModel();
        if (CharSequenceUtil.isNotBlank(smsContentModel.getUrl())) {
            return smsContentModel.getContent() + CharSequenceUtil.SPACE + smsContentModel.getUrl();
        } else {
            return smsContentModel.getContent();
        }
    }

    /**
     * 短信无撤回
     *
     * @param recallTaskInfo
     */
    @Override
    public void recall(RecallTaskInfo recallTaskInfo) {

    }
}
